<# .SYNOPSIS Configure User Rights Assignment policies using simplified positional arguments with optimized batch processing. .DESCRIPTION This script configures Windows User Rights Assignment policies under Local Policies → User Rights Assignment. These policies control which users and groups have specific privileges and rights on the system. USER RIGHTS ARE NOW PROCESSED IN ALPHABETICAL ORDER (as shown in GPO): - The assignment order matches the alphabetical display in Group Policy Editor for easier mapping and review. OPTIMIZATION FEATURES: - Single INF file creation for all user rights (batch processing) - Append, Overwrite, or Remove modes for flexible policy management - Backup current settings before changes - Efficient single secedit operation INPUT FORMAT: The script supports TWO input formats: 1. JSON Array Format (recommended for complex assignments): '["Administrators,Users","Users","Remote Desktop Users","Backup Operators","Guest","","","NT SERVICE\MyService","Backup Operators","Power Users"]' - Use double quotes inside the JSON array for each value - Separate multiple users/groups within a value using commas - Use empty strings "" to skip a right 2. Positional Arguments (direct command line): "Administrators,Users" "Users" "Remote Desktop Users" "Backup Operators" "Guest" "" "" "NT SERVICE\MyService" "Backup Operators" "Power Users" - Each argument corresponds to a user right in order - Separate multiple users/groups within an argument using commas - Use empty strings "" to skip a right Each value should contain the users/groups to assign the right to, separated by commas. Use standard formats like: - "Administrator,Administrators" (built-in accounts/groups) - "DOMAIN\User,BUILTIN\Administrators" (domain accounts) - "S-1-5-32-544" (SIDs) - "" (empty string to skip this right in current operation) - "CLEAR" (explicitly clear/disable the right) MODES: -Mode "Overwrite" (default): Replace existing user assignments -Mode "Append": Add users to existing assignments (merge with current) -Mode "Remove": Remove specified users from existing assignments USER RIGHTS (5 most commonly used rights - LIVE): 1. Access this computer from the network 2. Allow log on locally 3. Allow log on through Remote Desktop Services 4. Back up files and directories 5. Deny access to this computer from the network UPCOMING USER RIGHTS (42 additional rights - will be available in future releases): - Allow log on through Remote Desktop Services - Deny log on locally - Deny log on through Remote Desktop Services - Log on as a service - Restore files and directories - Shut down the system - Access Credential Manager as a trusted caller - Act as part of the operating system - Add workstations to domain - Adjust memory quotas for a process - Bypass traverse checking - Change the system time - Change the time zone - Create a pagefile - Create global objects - Create permanent shared objects - Create symbolic links - Create a token object - Debug programs - Deny log on as a batch job - Deny log on as a service - Enable computer and user accounts to be trusted for delegation - Force shutdown from a remote system - Generate security audits - Impersonate a client after authentication - Increase a process working set - Increase scheduling priority - Load and unload device drivers - Lock pages in memory - Log on as a batch job - Log on as a network service - Manage auditing and security log - Modify an object label - Modify firmware environment values - Perform volume maintenance tasks - Profile single process - Profile system performance - Remove computer from docking station - Replace a process level token - Synchronize directory service data - Take ownership of files or other objects .PARAMETER Mode Operation mode: "Overwrite" (default), "Append", or "Remove" - Overwrite: Replace existing assignments (default behavior) - Append: Add users to existing assignments without removing current ones - Remove: Remove specified users from existing assignments .PARAMETER BackupPath Optional path to save backup of current user rights before changes. Default: Creates timestamped backup in temp directory. .PARAMETER Arguments 5 positional string arguments corresponding to each user right (most commonly used). Supports both JSON array format and positional arguments. Specify users/groups as comma-separated values, "" to skip, or "CLEAR" to explicitly clear the right. .EXAMPLE # JSON Array Format - Overwrite mode (default) - Replace existing assignments .\Set-UserRightsAssignment.ps1 '["Administrators,Users","Users","Remote Desktop Users","Backup Operators","Guest"]' -Mode "Overwrite" .EXAMPLE # Positional Arguments - Overwrite mode .\Set-UserRightsAssignment.ps1 -Mode "Overwrite" "Administrators,Users" "Users" "Remote Desktop Users" "Backup Operators" "Guest" .EXAMPLE # JSON Array Format - Append mode - Add users to existing assignments .\Set-UserRightsAssignment.ps1 '["NewGroup","","DeveloperGroup","",""]' -Mode "Append" .EXAMPLE # Positional Arguments - Append mode .\Set-UserRightsAssignment.ps1 -Mode "Append" "NewGroup" "" "DeveloperGroup" "" "" .EXAMPLE # JSON Array Format - Remove mode - Remove specific users from assignments .\Set-UserRightsAssignment.ps1 '["Guest","","OldGroup","",""]' -Mode "Remove" .EXAMPLE # Positional Arguments - Remove mode .\Set-UserRightsAssignment.ps1 -Mode "Remove" "Guest" "" "OldGroup" "" "" .EXAMPLE # JSON Array Format - Clear specific rights explicitly .\Set-UserRightsAssignment.ps1 '["CLEAR","","","","CLEAR"]' .EXAMPLE # Positional Arguments - Clear specific rights .\Set-UserRightsAssignment.ps1 "CLEAR" "" "" "" "CLEAR" .NOTES - Requires administrative privileges (Run as Administrator) - Uses Windows secedit.exe and security templates internally - Changes require system restart or gpupdate to fully take effect - Be very careful with user rights as incorrect configuration can lock out access - Always test in non-production environment first - Some rights may conflict with security policies #> param( [Parameter(Position=0, ValueFromRemainingArguments=$true)] [string[]]$PolicyValuesArray = @("[]"), [string]$BackupPath = "", [ValidateSet('Silent','Normal','Verbose','Debug')] [string]$LogLevel = 'Normal', [ValidateSet("Overwrite", "Append", "Remove")] [string]$Mode = "Append", [string]$LogPath = $null, [switch]$WhatIf ) # Combine all arguments into a single PolicyValues string # First, try to get the original command line with proper quotes $PolicyValues = $null try { $currentPID = $PID Write-Host "Current Process ID: $currentPID" -ForegroundColor Cyan $process = Get-CimInstance Win32_Process -Filter "ProcessId = $currentPID" if ($process) { $commandLine = $process.CommandLine Write-Host "Full command line: $commandLine" -ForegroundColor Yellow if ($commandLine) { $scriptName = [System.IO.Path]::GetFileName($MyInvocation.MyCommand.Path) $escapedScriptName = [regex]::Escape($scriptName) $pattern = "-File\s+`"[^`"]*\\$escapedScriptName`"\s+(.+?)(?:\s+(?:-BackupPath|-LogLevel|-Mode|-LogPath|-WhatIf)|$)" Write-Host "Using regex pattern: $pattern" -ForegroundColor DarkGray if ($commandLine -match $pattern) { $rawArgument = $matches[1].Trim() Write-Host "Raw argument extracted: $rawArgument" -ForegroundColor Magenta if ($rawArgument -match '^"(.*)"$') { $PolicyValues = $matches[1] } else { $PolicyValues = $rawArgument } Write-Host "Extracted PolicyValues from command line: $PolicyValues" -ForegroundColor Green } else { Write-Host "Command line regex did not match. Command line: $commandLine" -ForegroundColor Red } } else { Write-Host "CommandLine property is null or empty" -ForegroundColor Red } } else { Write-Host "Failed to get process information for PID $currentPID" -ForegroundColor Red } } catch { Write-Host "Error extracting from command line: $($_.Exception.Message)" -ForegroundColor Red Write-Verbose "Could not extract from command line: $($_.Exception.Message)" } if (-not $PolicyValues) { $PolicyValues = if ($PolicyValuesArray.Count -gt 1) { $PolicyValuesArray -join '' } else { $PolicyValuesArray[0] } Write-Verbose "Using parameter-based PolicyValues: $PolicyValues" } # User Rights Assignment Database - Top 5 Most Commonly Used User Rights (sorted alphabetically by Name) # NOTE: Additional 42 user rights are available but commented out for future releases $UserRights = @( @{ Name = "Access this computer from the network"; Key = "SeNetworkLogonRight" }, @{ Name = "Allow log on locally"; Key = "SeInteractiveLogonRight" }, @{ Name = "Allow log on through Remote Desktop Services"; Key = "SeRemoteInteractiveLogonRight" }, @{ Name = "Back up files and directories"; Key = "SeBackupPrivilege" }, @{ Name = "Deny access to this computer from the network"; Key = "SeDenyNetworkLogonRight" } # UPCOMING USER RIGHTS (42 additional rights - commented out for future releases) # Uncomment these when ready to support additional user rights # @{ Name = "Deny log on locally"; Key = "SeDenyInteractiveLogonRight" }, # @{ Name = "Deny log on through Remote Desktop Services"; Key = "SeDenyRemoteInteractiveLogonRight" }, # @{ Name = "Log on as a service"; Key = "SeServiceLogonRight" }, # @{ Name = "Restore files and directories"; Key = "SeRestorePrivilege" }, # @{ Name = "Shut down the system"; Key = "SeShutdownPrivilege" }, # @{ Name = "Access Credential Manager as a trusted caller"; Key = "SeTrustedCredManAccessPrivilege" }, # @{ Name = "Act as part of the operating system"; Key = "SeTcbPrivilege" }, # @{ Name = "Add workstations to domain"; Key = "SeMachineAccountPrivilege" }, # @{ Name = "Adjust memory quotas for a process"; Key = "SeIncreaseQuotaPrivilege" }, # @{ Name = "Bypass traverse checking"; Key = "SeChangeNotifyPrivilege" }, # @{ Name = "Change the system time"; Key = "SeSystemtimePrivilege" }, # @{ Name = "Change the time zone"; Key = "SeTimeZonePrivilege" }, # @{ Name = "Create a pagefile"; Key = "SeCreatePagefilePrivilege" }, # @{ Name = "Create global objects"; Key = "SeCreateGlobalPrivilege" }, # @{ Name = "Create permanent shared objects"; Key = "SeCreatePermanentPrivilege" }, # @{ Name = "Create symbolic links"; Key = "SeCreateSymbolicLinkPrivilege" }, # @{ Name = "Create a token object"; Key = "SeCreateTokenPrivilege" }, # @{ Name = "Debug programs"; Key = "SeDebugPrivilege" }, # @{ Name = "Deny log on as a batch job"; Key = "SeDenyBatchLogonRight" }, # @{ Name = "Deny log on as a service"; Key = "SeDenyServiceLogonRight" }, # @{ Name = "Enable computer and user accounts to be trusted for delegation"; Key = "SeEnableDelegationPrivilege" }, # @{ Name = "Force shutdown from a remote system"; Key = "SeRemoteShutdownPrivilege" }, # @{ Name = "Generate security audits"; Key = "SeAuditPrivilege" }, # @{ Name = "Impersonate a client after authentication"; Key = "SeImpersonatePrivilege" }, # @{ Name = "Increase a process working set"; Key = "SeIncreaseWorkingSetPrivilege" }, # @{ Name = "Increase scheduling priority"; Key = "SeIncreaseBasePriorityPrivilege" }, # @{ Name = "Load and unload device drivers"; Key = "SeLoadDriverPrivilege" }, # @{ Name = "Lock pages in memory"; Key = "SeLockMemoryPrivilege" }, # @{ Name = "Log on as a batch job"; Key = "SeBatchLogonRight" }, # @{ Name = "Log on as a network service"; Key = "SeNetworkServiceLogonRight" }, # @{ Name = "Manage auditing and security log"; Key = "SeSecurityPrivilege" }, # @{ Name = "Modify an object label"; Key = "SeRelabelPrivilege" }, # @{ Name = "Modify firmware environment values"; Key = "SeSystemEnvironmentPrivilege" }, # @{ Name = "Perform volume maintenance tasks"; Key = "SeManageVolumePrivilege" }, # @{ Name = "Profile single process"; Key = "SeProfileSingleProcessPrivilege" }, # @{ Name = "Profile system performance"; Key = "SeSystemProfilePrivilege" }, # @{ Name = "Remove computer from docking station"; Key = "SeUndockPrivilege" }, # @{ Name = "Replace a process level token"; Key = "SeAssignPrimaryTokenPrivilege" }, # @{ Name = "Synchronize directory service data"; Key = "SeSyncAgentPrivilege" }, # @{ Name = "Take ownership of files or other objects"; Key = "SeTakeOwnershipPrivilege" } ) # Script-wide variables for SecpolFramework integration $script:LogFile = $null $script:StartTime = Get-Date $script:ProcessedCount = 0 $script:SuccessCount = 0 $script:FailureCount = 0 $script:SkippedCount = 0 # Initialize logging function from SecpolFramework function Initialize-LogPath { if ($LogPath) { $logDir = Split-Path $LogPath -Parent if ($logDir -and -not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } return $LogPath } # Try to get agent directory, fallback to script directory $baseDir = $PSScriptRoot try { $registryPath = if ([Environment]::Is64BitOperatingSystem) { "HKLM:\SOFTWARE\WOW6432Node\AdventNet\DesktopCentral\DCAgent" } else { "HKLM:\SOFTWARE\AdventNet\DesktopCentral\DCAgent" } $agentDir = Get-ItemProperty -Path $registryPath -Name "DCAgentInstallDir" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty DCAgentInstallDir if ($agentDir -and (Test-Path $agentDir)) { $baseDir = $agentDir } } catch { Write-Verbose "Using script directory for logs" } # Create log directory and file path $auditDir = Join-Path (Join-Path $baseDir "logs") "SecurityPolicies" if (-not (Test-Path $auditDir)) { New-Item -ItemType Directory -Path $auditDir -Force | Out-Null } $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" $scriptName = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.ScriptName) if ([string]::IsNullOrEmpty($scriptName)) { $scriptName = "Set-UserRightsAssignment" } return Join-Path $auditDir "${scriptName}_$timestamp.log" } $script:LogFile = try { Initialize-LogPath } catch { $null } # Logging Functions from SecpolFramework function Write-Log { param( [Parameter(Mandatory=$true)] [string]$Message, [ValidateSet('Info','Warning','Error','Debug','Success')] [string]$Level = 'Info', [string]$Component = 'UserRightsAssignment' ) $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' $logMessage = "[$timestamp] [$Level] [$Component] $Message" # Console output based on level and LogLevel setting switch ($Level) { 'Error' { if ($LogLevel -ne 'Silent') { Write-Error $Message } } 'Warning' { if ($LogLevel -notin @('Silent')) { Write-Warning $Message } } 'Success' { if ($LogLevel -notin @('Silent')) { Write-Host $Message -ForegroundColor Green } } 'Debug' { if ($LogLevel -eq 'Debug') { Write-Host $Message -ForegroundColor Gray } } 'Info' { if ($LogLevel -notin @('Silent')) { Write-Host $Message } } } # File output if LogPath is specified if ($script:LogFile) { Add-Content -Path $script:LogFile -Value $logMessage -Encoding UTF8 } } function Write-ProgressLog { param( [int]$Current, [int]$Total, [string]$Activity = "Processing User Rights", [string]$CurrentItem = "" ) if ($LogLevel -ne 'Silent') { $percentComplete = if ($Total -gt 0) { ($Current / $Total) * 100 } else { 0 } Write-Progress -Activity $Activity -Status "Processing $Current of $Total - $CurrentItem" -PercentComplete $percentComplete } } function Test-Admin { $id = [Security.Principal.WindowsIdentity]::GetCurrent() $p = New-Object Security.Principal.WindowsPrincipal($id) return $p.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) } function Initialize-Script { Write-Log "========== User Rights Assignment Configuration Started ==========" Write-Log "Script: Set-UserRightsAssignment.ps1" Write-Log "User: $env:USERNAME" Write-Log "Computer: $env:COMPUTERNAME" Write-Log "PowerShell Version: $($PSVersionTable.PSVersion)" Write-Log "Log Level: $LogLevel" Write-Log "Mode: $Mode" if ($script:LogFile) { Write-Log "Log File: $($script:LogFile)" } else { Write-Log "Logging: Console only" } if ($WhatIf) { Write-Log "WhatIf mode enabled - no changes will be applied" -Level Warning } if (-not (Test-Admin)) { Write-Log "Administrator privileges required. Please run this script as Administrator." -Level Error throw "Administrator privileges required" } Write-Log "Administrator check passed" -Level Success Write-Log "User Rights Database contains $($UserRights.Count) rights" -Level Success return $true } if (-not (Initialize-Script)) { return } # Initialize secedit file paths like SecpolFramework $exportPath = Join-Path -Path $PSScriptRoot -ChildPath "secpol_export.inf" $modifiedPath = Join-Path -Path $PSScriptRoot -ChildPath "secpol_modified.inf" Write-Log "Exporting current security policy to: $exportPath" -Level Debug secedit /export /cfg $exportPath | Out-Null # Import INF file function from SecpolFramework function Import-InfFile { param ([string]$filePath) $data = @{} $currentSection = "" foreach ($line in Get-Content $filePath) { $line = $line.Trim() if ($line -match "^\[(.+)\]$") { $currentSection = $matches[1] if (-not $data.ContainsKey($currentSection)) { $data[$currentSection] = @{} } } elseif ($line -and -not $line.StartsWith(";") -and $currentSection) { $splitIndex = $line.IndexOf("=") if ($splitIndex -gt 0) { $key = $line.Substring(0, $splitIndex).Trim() $value = $line.Substring($splitIndex + 1).Trim() $data[$currentSection][$key] = $value } } } return $data } # Write INF file function from SecpolFramework function Write-InfFile { param ( [hashtable]$policyData, [string]$filePath ) $content = @() if ($policyData.ContainsKey('Unicode')) { $content += "[Unicode]" foreach ($key in $policyData['Unicode'].Keys) { $content += "$key = $($policyData['Unicode'][$key])" } $content += "" } foreach ($section in $policyData.Keys | Where-Object { $_ -ne 'Unicode' -and $_ -ne 'Version' }) { $content += "[$section]" foreach ($key in $policyData[$section].Keys) { $content += "$key = $($policyData[$section][$key])" } $content += "" } if ($policyData.ContainsKey('Version')) { $content += "[Version]" foreach ($key in $policyData['Version'].Keys) { $content += "$key = $($policyData['Version'][$key])" } $content += "" } $content | Out-File -FilePath $filePath -Encoding ASCII } $policyData = Import-InfFile -filePath $exportPath function Get-CurrentUserRights { Write-Log "Retrieving current user rights configuration from policy data..." -Level Info try { $currentRights = @{} # Extract Privilege Rights section from imported policy data if ($policyData.ContainsKey('Privilege Rights')) { $currentRights = $policyData['Privilege Rights'] Write-Log "Successfully retrieved current user rights for $($currentRights.Count) policies" -Level Success } else { Write-Log "No Privilege Rights section found in policy data" -Level Warning } return $currentRights } catch { Write-Log "Error retrieving current user rights: $_" -Level Error return @{} } } function Merge-UserRights { param( [string]$Current, [string]$New, [string]$Mode ) if ([string]::IsNullOrEmpty($New) -or $New -eq "CLEAR") { if ($Mode -eq "Overwrite" -or $New -eq "CLEAR") { return "" # Clear the right } else { return $Current # Keep current if not overwriting } } # Split current users by comma $currentUsers = if ([string]::IsNullOrEmpty($Current)) { @() } else { $Current -split ',' | ForEach-Object { $_.Trim() } } # Replace literal \n and actual newlines with commas, then split and convert to SIDs $New = $New -replace '\\n', ',' -replace "`r`n", ',' -replace "`n", ',' $newUsers = $New -split ',' | ForEach-Object { $_.Trim() } switch ($Mode) { "Overwrite" { return ($newUsers -join ',') } "Append" { $mergedUsers = $currentUsers + $newUsers | Sort-Object -Unique return ($mergedUsers -join ',') } "Remove" { $remainingUsers = $currentUsers | Where-Object { $_ -notin $newUsers } return ($remainingUsers -join ',') } default { return ($newUsers -join ',') } } } function Set-AllUserRights { param( [hashtable]$CurrentRights, [array]$NewRights, [string]$Mode ) Write-Log "Creating optimized security template for batch processing..." -Level Info # Generate timestamp for backup $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" try { # Create backup if requested if (-not [string]::IsNullOrEmpty($BackupPath) -or $BackupPath -eq "") { $backupFile = if ([string]::IsNullOrEmpty($BackupPath)) { "$env:TEMP\UserRights_Backup_$timestamp.inf" } else { $BackupPath } Write-Log "Creating backup at: $backupFile" -Level Info $result = & secedit.exe /export /cfg $backupFile 2>&1 if ($LASTEXITCODE -eq 0) { Write-Log "Backup created successfully" -Level Success } else { Write-Log "Failed to create backup: $result" -Level Warning } } # Ensure Privilege Rights section exists in policy data if (-not $policyData.ContainsKey('Privilege Rights')) { Write-Log "Creating new Privilege Rights section" -Level Info $policyData['Privilege Rights'] = @{} } $changesCount = 0 for ($i = 0; $i -lt [Math]::Min($UserRights.Count, $NewRights.Count); $i++) { Write-ProgressLog -Current ($i + 1) -Total $UserRights.Count -CurrentItem $UserRights[$i].Name $right = $UserRights[$i] $newValue = $NewRights[$i] # Skip empty arguments (not explicitly cleared) if ([string]::IsNullOrEmpty($newValue)) { $script:SkippedCount++ continue } $currentValue = $CurrentRights[$right.Key] $mergedValue = Merge-UserRights -Current $currentValue -New $newValue -Mode $Mode # Update the policy data directly $policyData['Privilege Rights'][$right.Key] = $mergedValue $changesCount++ $script:SuccessCount++ Write-Log "[$($i+1)] $($right.Name)" -Level Info Write-Log " Current: $($currentValue -replace '^$', '(none)')" -Level Debug Write-Log " New: $($mergedValue -replace '^$', '(none)')" -Level Info Write-Log " Mode: $Mode" -Level Debug } if ($changesCount -eq 0) { Write-Log "No changes to apply." -Level Warning return $true } # Write the updated INF file using framework method Write-InfFile -policyData $policyData -filePath $modifiedPath Write-Log "Updated INF file saved to $modifiedPath" -Level Success Write-Log "Applying $changesCount user rights changes in single operation..." -Level Info # Apply all changes in one secedit operation (skip if WhatIf) if (-not $WhatIf) { $result = & secedit.exe /configure /db secedit.sdb /cfg $modifiedPath /areas USER_RIGHTS 2>&1 if ($LASTEXITCODE -eq 0) { Write-Log "Successfully applied all user rights changes" -Level Success Write-Log "Changes applied: $changesCount user rights" -Level Success return $true } else { Write-Log "Failed to apply user rights changes. Exit code: $LASTEXITCODE" -Level Error Write-Log "Secedit output: $result" -Level Error return $false } } else { Write-Log "WHATIF: Would apply $changesCount user rights changes" -Level Info return $true } } catch { Write-Log "Error applying user rights: $_" -Level Error return $false } } # Main execution with SecpolFramework logging Write-Log "Starting user rights assignment processing for $($UserRights.Count) rights" Write-Log "Operation Mode: $Mode" -Level Info Write-Log "WARNING: User Rights Assignment changes can affect system security and access!" -Level Warning Write-Log "Make sure you have alternative administrative access before proceeding." -Level Warning # Parse PolicyValues array string to array function Parse-PolicyValuesArray { param([string]$ArrayString) # Check if it looks like JSON format if ($ArrayString -match '^\s*\[.*\]\s*$') { try { # Parse the JSON-like array string $parsedArguments = ConvertFrom-Json $ArrayString Write-Log "Successfully parsed policy values array: $($parsedArguments.Count) values provided" -Level Info return $parsedArguments } catch { Write-Log "Failed to parse PolicyValues array string: $($_.Exception.Message)" -Level Error Write-Log "Expected format: '[\"value1\",\"value2\",\"value3\"]'" -Level Warning return @() } } else { # Not JSON format - use PolicyValuesArray directly as positional arguments Write-Log "Using positional arguments format (non-JSON): $($PolicyValuesArray.Count) values provided" -Level Info return $PolicyValuesArray } } # Parse PolicyValues to get individual arguments $arguments = Parse-PolicyValuesArray -ArrayString $PolicyValues Write-Log "Arguments provided: $($arguments.Count)" -Level Info if ($arguments.Count -eq 0) { Write-Log "No user rights arguments provided. Please specify user assignments for the rights you want to modify." -Level Error Write-Log "Usage Examples:" -Level Info Write-Log ' .\Set-UserRightsAssignment.ps1 -Mode "Overwrite" "" "Administrators,Users" "" "" "Administrators"' -Level Info Write-Log ' .\Set-UserRightsAssignment.ps1 -Mode "Append" "" "NewGroup" "" "" ""' -Level Info Write-Log ' .\Set-UserRightsAssignment.ps1 -Mode "Remove" "" "OldGroup" "" "" ""' -Level Info return } # Get current user rights configuration $currentRights = Get-CurrentUserRights if ($currentRights.Count -eq 0) { Write-Log "Failed to retrieve current user rights. Cannot proceed safely." -Level Error return } # Apply all changes in optimized batch operation $success = Set-AllUserRights -CurrentRights $currentRights -NewRights $arguments -Mode $Mode # Final summary using SecpolFramework pattern function Write-CompletionSummary { $endTime = Get-Date $duration = $endTime - $script:StartTime Write-Log "========== User Rights Assignment Configuration Summary ==========" -Level Info Write-Log "Execution Duration: $($duration.ToString('hh\:mm\:ss'))" -Level Info Write-Log "Total User Rights: $($UserRights.Count)" -Level Info Write-Log "Successfully Applied: $script:SuccessCount" -Level Success Write-Log "Failed: $script:FailureCount" -Level $(if ($script:FailureCount -gt 0) { 'Warning' } else { 'Info' }) Write-Log "Not Configured: $script:SkippedCount" -Level Info Write-Log "Mode Used: $Mode" -Level Info $actuallyProcessed = $script:SuccessCount + $script:FailureCount if ($actuallyProcessed -gt 0) { $successRate = [math]::Round(($script:SuccessCount / $actuallyProcessed) * 100, 2) Write-Log "Success Rate: $successRate% (of actually processed rights)" -Level Info } if ($script:FailureCount -gt 0) { Write-Log "Some user rights failed to apply. Check the log for details." -Level Warning Write-Log "Common issues: Invalid values, insufficient permissions, secedit errors" -Level Warning } if ($WhatIf) { Write-Log "WhatIf mode was enabled - no actual changes were made." -Level Info } Write-Log "IMPORTANT NOTES:" -Level Info Write-Log "- Changes may require system restart or 'gpupdate /force' to take full effect" -Level Info Write-Log "- Verify administrative access still works after changes" -Level Warning Write-Log "- Backup was created automatically in temp directory" -Level Info Write-Log "- Test all critical functionality after applying changes" -Level Warning Write-Log "- Use 'secedit /export /cfg current.inf' to verify applied settings" -Level Info if ($script:LogFile) { Write-Log "Detailed log saved to: $script:LogFile" -Level Info } Write-Log "========== User Rights Assignment Configuration Complete ==========" -Level Info } Write-CompletionSummary if ($script:FailureCount -gt 0) { exit 1 } else { exit 0 }